﻿using Newtonsoft.Json.Linq;
using SharpFont;
using SharpFont.Internal;
using System.Runtime.InteropServices;
using System.Text;
using ttbit.form;

namespace fontconv
{
    internal class Program
    {
        static void Main(string[] args)
        {
            ///Check args
            if (args.Length < 1)
            {
                Console.WriteLine("Used like below,give font name and font size");
                Console.WriteLine("FontConv Arial.ttf 12");
                return;
            }

            var filename = args[0];
            if (System.IO.File.Exists(filename) == false)
            {
                Console.WriteLine("Font file error.");
                return;
            }
            int fontsize = 16;
            if (args.Length > 1)
            {
                if (!int.TryParse(args[1], out fontsize))
                {
                    Console.WriteLine("Font size error.");
                    return;
                }
                if (fontsize < 8 || fontsize > 128)
                {
                    Console.WriteLine("Font size should 8~128 default 16");
                    return;

                }
            }
            int border = 0;
            if (args.Length > 2)
            {
                if (!int.TryParse(args[2], out border))
                {
                    Console.WriteLine("Font border error.");
                    return;
                }
                if (border > 128)
                {
                    Console.WriteLine("Font border should <128 default 0");
                    return;

                }
            }
            //Error err = FT.FT_Init_FreeType(out ptrLibrary);
            //err = FT.FT_New_Face(ptrLibrary, filename, 0, out ptrFace);
            //rec = Marshal.PtrToStructure<FaceRec>(ptrFace);
            //err = FT.FT_Set_Char_Size(ptrFace, (IntPtr)0, (IntPtr)fontsize, 0, 96);


            unsafe
            {
                //uint*  ptr = (uint*)(void*)rec.num_glyphs;

            }
            library = new SharpFont.Library();
            renderer = library.GetRenderer(GlyphFormat.Bitmap);
            face = new SharpFont.Face(library, filename, 0);

            //face.SetCharSize(0, Fixed26Dot6.FromDecimal(fontsize), 0, 96);
            face.SetPixelSizes((uint)fontsize, (uint)fontsize);
            Console.WriteLine("font=" + face.FamilyName + "," + face.StyleName);

            FontData fontFormat = new FontData();
            fontFormat.header = new FontData.Header();
            fontFormat.fontItems = new Dictionary<uint, FontData.Item>();
            fontFormat.header.fontSize = (byte)fontsize;
            fontFormat.header.fontBorder = (byte)border;
            
            //var json = new JObject();
            //json["fontname"] = face.FamilyName;
            //json["fontstyle"] = face.StyleName;
            //json["fontsize"] = fontsize;
            //charinfo = new JArray();
            //json["charmap"] = charinfo;
            //face.SetCharSize(0, Fixed26Dot6.FromInt32(fontsize * 64), 0, 96);
            //face.SetPixelSizes((uint)fontsize, (uint)fontsize);
            Dictionary<uint, uint> mapChar2Glyph = new Dictionary<uint, uint>();
            {
                uint glyphIndex;
                //var c = FT.FT_Get_First_Char(ptrFace, out glyphIndex);
                var c = face.GetFirstChar(out glyphIndex);
                while (true)
                {
                    mapChar2Glyph[c] = glyphIndex;
                    //c = FT.FT_Get_Next_Char(ptrFace, c, out glyphIndex);
                    c = face.GetNextChar(c, out glyphIndex);
                    if (c == 0)
                        break;
                }
            }
            Console.WriteLine("Parse font:charcount=" + mapChar2Glyph.Count);
            System.IO.File.Delete("error.log");
            foreach (var item in mapChar2Glyph)
            {
                var itembin = ParseChar(item.Key, item.Value, fontFormat.header);
                fontFormat.fontItems[itembin.charCode] = itembin;
            }
            var outfilename = "font_" + fontsize + ".ttfont";
            System.IO.File.Delete(outfilename);
            using (var f = System.IO.File.OpenWrite(outfilename))
            {
                using var ws = new System.IO.BinaryWriter(f);
                fontFormat.Write(ws);
            }

            //test
            var bs = System.IO.File.ReadAllBytes(outfilename);
            FontData f2 = new FontData();
            using (var ms = new System.IO.MemoryStream(bs))
            {
                using (var reader = new System.IO.BinaryReader(ms))
                {
                    f2.Read(reader);
                }
            }
            Console.WriteLine("done writeto=" + outfilename);
            if (bitmaperr > 0)
            {
                Console.WriteLine("其中bitmap有问题的有" + bitmaperr + " 详情见error.log");
            }
            if (dumperror)
            {
                Console.WriteLine(" 详情见error.log");
            }
            //{
            //    uint glyphIndex;
            //    //var c = FT.FT_Get_First_Char(ptrFace, out glyphIndex);
            //    var c = face.GetFirstChar(out glyphIndex);
            //    var jsonitem = ParseChar(c, glyphIndex, fontsize);
            //    if (jsonitem != null)
            //        charinfo.Add(jsonitem);
            //    while (true)
            //    {
            //        //c = FT.FT_Get_Next_Char(ptrFace, c, out glyphIndex);
            //        c = face.GetNextChar(c, out glyphIndex);
            //        if (c == 0)
            //            break;
            //        if (c == 224)
            //        {

            //        }
            //        jsonitem = ParseChar(c, glyphIndex, fontsize);
            //        if (jsonitem != null)
            //            charinfo.Add(jsonitem);
            //    }
            //}



        }
        //static IntPtr ptrLibrary;
        //static IntPtr ptrFace;
        //static FaceRec rec;
        static SharpFont.Library library;
        static SharpFont.Face face;
        static SharpFont.Renderer renderer;
        static int bitmaperr = 0;
        static bool dumperror = false;
        static ttbit.form.FontData.Item ParseChar(uint charcode, uint glyphIndex, FontData.Header header)
        {
            FontData.Item item = new FontData.Item();

            //Console.WriteLine("GetChar[" + charindex + "]:" + (char)charcode);


            face.LoadGlyph(glyphIndex, LoadFlags.NoAutohint | LoadFlags.Monochrome, LoadTarget.Mono);
            var g = face.Glyph;
            if (g == null)
                return null;

            item.charCode = charcode;
            var size = g.Metrics;
            item.emptyX = (sbyte)size.HorizontalBearingX.ToDecimal();
            if (item.emptyX < 0)
            {

            }
        
            item.emptyY = (sbyte)(size.VerticalAdvance.ToDecimal()- size.HorizontalBearingY.ToDecimal());// (sbyte)(header.fontSize - (int)size.HorizontalBearingY.ToDecimal());
            if (item.emptyY < 0)
            {

            }
            item.width = (byte)size.Width.ToDecimal();
            item.height = (byte)size.Height.ToDecimal();
            item.advanceX = (byte)size.HorizontalAdvance.ToDecimal();
            item.graydata = new byte[header.fontBitmapSize * header.fontBitmapSize];
            var w = g.Metrics.Width;
            var h = g.Metrics.Height;
            face.Glyph.OwnBitmap();

            try
            {

                face.Glyph.RenderGlyph(RenderMode.Normal);
            }
            catch
            {
                return null;
            }
            var bw = face.Glyph.Bitmap.Pitch;
            var bh = face.Glyph.Bitmap.Rows;
            if (bw != item.width || bh != item.height)
                throw new Exception("error bitmap size");

            //修正一些超范围的数据
            if (item.width > header.fontBitmapSize)
                item.width = (byte)header.fontBitmapSize;
            if (item.height > header.fontBitmapSize)
                item.height = (byte)header.fontBitmapSize;
            if(item.advanceX<item.emptyX+item.width)
            {
                item.advanceX = (byte)(item.emptyX + item.width);
            }

            bool bitboom = bw > header.fontBitmapSize || bh > header.fontBitmapSize;
            if (bitboom)
            {
               

                bitmaperr++;
                //Console.WriteLine("error bitmap when parse:" + (char)charcode);
                if (dumperror)
                    System.IO.File.AppendAllText("error.log", "error bitmap when parse:" + (char)charcode + "\n");
            }


            for (var y = 0; y < bh; y++)
            {
                for (var x = 0; x < bw; x++)
                {
                    //int fy = bh - 1 - y;//左下角为0点
                    int fy = y;//左上角为0点
                    if (fy < header.fontBitmapSize && x < header.fontBitmapSize)
                    {
                        var p = face.Glyph.Bitmap.BufferData[y * face.Glyph.Bitmap.Pitch + x];
                        
                        item.graydata[fy * header.fontBitmapSize + x] = p;
                    }
                    else
                    {
                        bitboom = true;

                    }
                }
            }
            if (bitboom && dumperror)
            {

                for (var y = 0; y < bh; y++)
                {
                    string line = "";
                    for (var x = 0; x < header.fontBitmapSize; x++)
                    {

                        if (y < header.fontBitmapSize && item.graydata[y * header.fontBitmapSize + x] > (256 / 7))
                            line += item.graydata[y * header.fontBitmapSize + x].ToString("X02");
                        else
                            line += "  ";
                    }
                    for (var x = 0; x < bw; x++)
                    {
                        var p = face.Glyph.Bitmap.BufferData[y * face.Glyph.Bitmap.Pitch + x];
                        if (p > (256 / 7))
                            line += p.ToString("X02");
                        else
                            line += "  ";
                    }
                    System.IO.File.AppendAllText("error.log", line + "\n");
                }
            }

            return item;




        }
    }
}